//
// Created by Tony on 2024/6/30.
//

#include "Class.h"
#include "../rt_data/Object.h"
#include "AccessFlag.h"
#include "ClassTable.h"
#include "Method.h"
#include <memory>
#include <sstream>

namespace RtCode {

using std::make_shared;

Class::Class(string name, const shared_ptr<ClassTable>& classTable)
    : mName(std::move(name)), mClassTable(classTable) {}

ConstantPool& Class::getConstantPool() {
    if (mConstantPool == nullptr) {
        mConstantPool = make_shared<ConstantPool>(shared_from_this());
    }
    return *mConstantPool;
}

void Class::addMethod(const shared_ptr<Method>& method) {
    method->setOwnerClass(shared_from_this());
    mMethods.push_back(method);
}

void Class::addField(const shared_ptr<Field>& field) {
    if (field->isStatic()) {
        field->setSlotId(mStaticSlotCount++);
        mStaticVars.resize(mStaticSlotCount);
    } else {
        field->setSlotId(mInstanceSlotCount++);
    }
    field->setOwnerClass(shared_from_this());
    mFields.push_back(field);
}

std::shared_ptr<Field> Class::getField(const string& name) {
    for (auto& field : mFields) {
        if (field->getName() == name) {
            return field;
        }
    }
    return nullptr;
}

shared_ptr<Method> Class::getClassInitMethod() {
    return getMethod(Runtime::CLASS_INIT_METHOD_NAME);
}

shared_ptr<Method> Class::getMainMethod() { return getMethod(Runtime::CLASS_MAIN_METHOD_NAME); }

std::shared_ptr<Method> Class::getObjectInitMethod() {
    return getMethod(Runtime::OBJECT_INIT_METHOD_NAME);
}

shared_ptr<Method> Class::getMethod(const string& name) {
    for (auto& method : mMethods) {
        if (method->getName() == name) {
            return method;
        }
    }
    return nullptr;
}

RtData::Object* Class::newObject() { return new RtData::Object(this, mInstanceSlotCount); }

shared_ptr<Compiler::SymbolTable> Class::getSymbolTable() {
    auto symbolTable =
        make_shared<Compiler::SymbolTable>(Compiler::ScopeType::IMPORTS, 0, nullptr, mName);
    for (auto& field : mFields) {
        symbolTable->add(field->getName(), Compiler::SymbolType::FIELD, field->getType());
    }
    for (auto& method : mMethods) {
        symbolTable->add(method->getName(), Compiler::SymbolType::METHOD, method->getReturnType());
    }
    return symbolTable;
}

string Class::toString() {
    using std::endl;
    std::stringstream ss;
    ss << "class: " << getName() << endl;
    ss << mAccessFlags.toString() << endl;
    ss << "instance_slots=" << mInstanceSlotCount << ", static_slots=" << mStaticSlotCount << endl;
    ss << endl << getConstantPool().toString();
    for (const auto& method : mMethods) {
        ss << endl << method->toString();
    }
    return ss.str();
}

} // namespace RtCode